package top.codedance.iotp.client.plugins.async.opc;

import cd.io.netty.channel.ChannelHandlerContext;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import top.codedance.iotp.client.ClientApplication;
import top.codedance.iotp.common.executer.AsyncAbstractExecuter;
import top.codedance.iotp.common.entity.CommonEntity;
import org.apache.log4j.Logger;
import org.ini4j.Ini;
import org.ini4j.Wini;

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class OpcExecuterImpl extends AsyncAbstractExecuter {
    private Logger logger = ClientApplication.getLogger(OpcExecuterImpl.class);
    private static ExecutorService opcPools = Executors.newCachedThreadPool();
    private static Map<Integer, Service> opcs = new HashMap<>();
    public static volatile Map<String, Set<String>> tasks = new HashMap<>();

    @Override
    public void exec(ChannelHandlerContext channelHandlerContext, CommonEntity commonEntity, String taskId) {
        super.exec(channelHandlerContext, commonEntity, taskId);
        opcPools.submit(() -> {
            JSONObject requestBodyJSONObject = getRequestBodyJSONObject();
            Integer instance = requestBodyJSONObject.getInteger("instance");
            tryCreateConnect(this, instance);
            if(requestBodyJSONObject.containsKey("body")){
                synchronized (OpcExecuterImpl.class) {
                    JSONArray body = requestBodyJSONObject.getJSONArray("body");
                    Service service = opcs.get(instance);
                    String tag;
                    for (int i = 0; i < body.size(); i++) {
                        tag = body.getString(i);
                        try{
                            JSON.parseObject(tag);
                            service.send(body);
                            break;
                        }catch (Exception ex){
                            if (!tasks.containsKey(tag)) {
                                tasks.put(tag, new LinkedHashSet<>());
                            }
                            tasks.get(tag).add(getClientId());
                            service.subscribe();
                        }
                    }
                }
            }
            if(requestBodyJSONObject.containsKey("release") && requestBodyJSONObject.getBoolean("release")){
                synchronized (OpcExecuterImpl.class) {
                    Set<Map.Entry<String, Set<String>>> entries = tasks.entrySet();
                    for (Map.Entry<String, Set<String>> entry : entries) {
                        try {
                            entry.getValue().remove(getClientId());
                        } catch (Exception e) {
                        }
                    }
                }
            }
        });
    }

    private synchronized void tryCreateConnect(AsyncAbstractExecuter executer, Integer instance){
        if(!opcs.containsKey(instance)) {
            Service service = null;
            Wini ini = getIniConfig();
            if (ini != null && ini.containsKey("opc")) {
                OpcServiceParam opcServerParam = new OpcServiceParam();
                String type = "da";
                Ini.Section section = ini.get("opc");
                if (section.containsKey("type." + instance)) {
                    type = section.get("type." + instance);
                }
                if (type.equals("da")) {
                    if (section.containsKey("host." + instance)) {
                        opcServerParam.setHost(section.get("host." + instance));
                    }
                    if (section.containsKey("domain." + instance)) {
                        opcServerParam.setDomain(section.get("domain." + instance));
                    }
                    if (section.containsKey("progId." + instance)) {
                        opcServerParam.setProgId(section.get("progId." + instance));
                    }
                    if (section.containsKey("user." + instance)) {
                        opcServerParam.setUser(section.get("user." + instance));
                    }
                    if (section.containsKey("password." + instance)) {
                        opcServerParam.setPassword(section.get("password." + instance));
                    }
                    service = new DaOpcService(executer, opcServerParam);
                }
                if (type.equals("ua")) {
                    if (section.containsKey("endpointUrl." + instance)) {
                        opcServerParam.setEndpointUrl(section.get("endpointUrl." + instance));
                    }
                    if (section.containsKey("securityPolicy." + instance)) {
                        opcServerParam.setSecurityPolicy(BmSecurityPolicy.valueOf(Integer.parseInt(section.get("securityPolicy." + instance))));
                    }
                    if (section.containsKey("strength." + instance)) {
                        opcServerParam.setStrength(Integer.parseInt(section.get("strength." + instance)));
                    }
                    if (section.containsKey("algorithm." + instance)) {
                        opcServerParam.setAlgorithm(section.get("algorithm." + instance));
                    }
                    if (section.containsKey("validity." + instance)) {
                        opcServerParam.setValidity(Integer.parseInt(section.get("validity." + instance)));
                    }
                    if (section.containsKey("namespace." + instance)) {
                        opcServerParam.setNamespace(Integer.parseInt(section.get("namespace." + instance)));
                    }
                    if (section.containsKey("user." + instance)) {
                        opcServerParam.setUser(section.get("user." + instance));
                    }
                    if (section.containsKey("password." + instance)) {
                        opcServerParam.setPassword(section.get("password." + instance));
                    }
                    service = new UaOpcService(executer, opcServerParam);
                }

                try {
                    service.createConnect();
                    opcs.put(instance, service);
                } catch (Exception e) {
                    logger.debug(e.getMessage());
                }
            }
        }
    }
}
